import os.path
import typing
from typing import Optional, Iterable

import pytest

from . import utils, runner, assertion

AssertionFunc = typing.Callable[[list[dict]], None]


def yaml_cases(
        cases_path: str | Iterable[str],
        global_vars_path: Optional[str] = None,
        recursion: bool = True,
        assertion_func: AssertionFunc | None = assertion.assert_all_pass,
):
    """
    装饰器： 用于收集yaml用例的路径

    :param assertion_func: 断言函数Callable[[list[dict]], None]，默认为assertion.assert_all_pass。
    可以设置成自定义的断言函数，如果设置为None则需要用户自己在测试函数中断言
    :param cases_path: 必须，用例文件路径或所在目录的可迭代对象
    :param global_vars_path: 可选，全局变量所在路径
    :param recursion: 可选，收集用例文件时，是否递归查找每个子目录，默认为True
    :return:
    """
    if isinstance(cases_path, str):
        cases_path = [cases_path]

    def decorator(test_func):
        yaml_files: list[str] = []

        for p in cases_path:
            if os.path.isfile(p):
                if not p.lower().endswith(('.yaml', '.yml')):
                    raise ValueError(f"File '{p}' is not a valid YAML file")
                yaml_files.append(p)
            elif os.path.isdir(p):
                yaml_files.extend(utils.get_yamlpath_recursion(p) if recursion else utils.get_yamlpath(p))
            else:
                raise ValueError(f"Path '{p}' is neither a file nor a directory")

        global_vars = {}
        if global_vars_path is not None:
            if not global_vars_path.lower().endswith(('.yaml', '.yml')):
                raise ValueError(f"Global vars file '{global_vars_path}' is not a YAML file")
            if not os.path.isfile(global_vars_path):
                raise FileNotFoundError(f"Global vars file '{global_vars_path}' does not exist")
            global_vars = utils.load_global_var(global_vars_path)

        @pytest.mark.parametrize('yaml_file', yaml_files, ids=yaml_files)
        def wrapper(yaml_file):
            results = runner.run(yaml_file, global_vars)
            test_func(results)
            if assertion_func is not None:
                assertion_func(results)

        return wrapper

    return decorator
